package com.meatball.iot.driver.dtu

import com.meatball.iot.common.ClientConnectionThreadPool
import com.meatball.iot.driver.dtu.data.document.DtuDocument
import com.meatball.iot.driver.dtu.repository.DtuRepository
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.io.PrintStream
import java.math.BigDecimal
import java.math.RoundingMode
import java.net.ServerSocket
import java.net.Socket
import java.nio.ByteBuffer
import java.nio.ByteOrder

/**
 * TODO socket服务端
 *
 * @author 张翔宇
 * @since 2024年5月10日
 */
@Service
class DtuServerService(private val dtuRepository: DtuRepository) {
    companion object {
        private val logger = LoggerFactory.getLogger(DtuServerService::class.java)
    }
    /**
     * 服务端口号
     */
//    @Value("\${socket.dtu}")
    private var port: Int = 8888

    /**
     * 创建一个固定大小的线程池，用于处理客户端连接
     */
    private val executorService = ClientConnectionThreadPool.executorService()

    /**
     * TODO 系统启动后自动运行
     *
     */
    fun startServer() {
        // 在一个新线程中启动服务
        executorService.execute {
            try {
                // 创建一个 ServerSocket 并绑定到指定的端口
                ServerSocket(port).use { serverSocket ->
                    logger.info("DTU监听服务启动成功：$port")
                    // 循环接受客户端连接
                    while (true) {
                        val clientSocket = serverSocket.accept()
                        // 处理客户端连接
                        handleClientConnection(clientSocket)
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    private fun handleClientConnection(clientSocket: Socket) {
        // 在一个新线程中处理客户端连接
        executorService.execute {
            try {
                // 获取客户端的输出流
                clientSocket.getOutputStream().use { outputStream ->
                    // 包装客户端的输出流为 PrintStream
                    PrintStream(outputStream).use { printStream ->
                        clientSocket.getInputStream().use { inputStream ->
                            val line = ByteArray(1024)
                            var bytesRead: Int
                            // 循环读取客户端发送的消息
                            while (inputStream.read(line).also { bytesRead = it } != -1) {
                                // 打印客户端发送的消息
                                val hexString = line.take(bytesRead).joinToString(" ") { String.format("%02X", it) }
                                logger.info("Received from client: $hexString")
                                if (hexString != "FF FF") {
                                    // 解析数据
                                    // 注意：因为这里每次读取的数据都可以完整地解析为我们想要的格式
                                    // 如果不是这种情况，则需要将数据缓存起来，直到有足够的数据可以解析
                                    parseData(line.take(bytesRead).toByteArray(), hexString)
                                }

                                // 将消息回显给客户端
                                printStream.println("Echo from server: Over")
                            }

                        }
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                // 关闭客户端连接
                clientSocket.close()
            }
        }
    }

    fun parseData(bytes: ByteArray, hexString: String) {
        // 解析设备ID
        val deviceID = bytes.sliceArray(1..4).joinToString("") { "%02X".format(it) }//.toInt(16)
        // 解析帧计数
        val frameCount = ByteBuffer.wrap(bytes, 7, 2).order(ByteOrder.LITTLE_ENDIAN).short.toInt()
        // 解析组活动总能量
        val groupActiveTotalEnergy = parseData(bytes, 17..20, 100.0, 2)
        // 解析A电压
        val aVoltage = parseData(bytes, 25..26, 10.0, 1)
        // 解析B电压
        val bVoltage = parseData(bytes, 31..32, 10.0, 1)
        // 解析C电压
        val cVoltage = parseData(bytes, 37..38, 10.0, 1)
        // 解析A电流
        val aCurrent = parseData(bytes, 43..45, 1000.0, 3)
        // 解析B电流
        val bCurrent = parseData(bytes, 50..52, 1000.0, 3)
//        // 解析C电流
        val cCurrent = parseData(bytes, 57..59, 1000.0, 3)
//        // 解析即时总有功功率
        val instantTotalActivePower = parseData(bytes, 64..66, 10000.0, 4)
//        // 解析总功率因数
        val totalPowerFactor = parseData(bytes, 71..72, 1000.0, 3)

        // 打印解析的数据
        logger.info("设备ID：$deviceID \n 帧计数：$frameCount \n 组合有功总电能: $groupActiveTotalEnergy \n " +
                "A相电压: ${aVoltage}V \n B相电压: ${bVoltage}V \n C相电压: ${cVoltage}V \n " +
                "A电流: ${aCurrent}A \n B电流: ${bCurrent}A \n C电流: ${cCurrent}A \n " +
                "瞬时总有功功率: ${instantTotalActivePower}W \n 总功率因数: $totalPowerFactor")

        dtuRepository.save(DtuDocument(
            deviceID = deviceID,
            frameCount = frameCount,
            groupActiveTotalEnergy = groupActiveTotalEnergy,
            aVoltage = aVoltage,
            bVoltage = bVoltage,
            cVoltage = cVoltage,
            aCurrent = aCurrent,
            bCurrent = bCurrent,
            cCurrent = cCurrent,
            instantTotalActivePower = instantTotalActivePower,
            totalPowerFactor = totalPowerFactor,
            rawData = hexString.replace(" ", "")
        ))
    }

    /**
     * TODO 解析数据
     *
     * @param bytes 字节数组
     * @param indices 区间
     * @param dividend 被除数
     * @param scale 保留小数
     */
    private fun parseData(bytes: ByteArray, indices: IntRange, dividend: Double, scale: Int): BigDecimal {
        return BigDecimal(bytes.sliceArray(indices)
            .reversedArray()
            .joinToString("") { "%02X".format(it) }.toInt() / dividend)
            .setScale(scale, RoundingMode.HALF_UP)
    }
}